#include "iap.h"
#include <string.h>
#include <usb_lib.h>
#include <usb_desc.h>
#include <usb_istr.h>
#include <usb_pwr.h>

#include "display.h"
#include "usb.h"
#include "vx.h"

#define	ROM_BASE	0x08000000

Jump*	tempVect=(Jump*)0x08008000;

static ubyte checkSum(ushort *rx,ushort ord){
	ubyte *p=(ubyte*)tagetBas;
	ubyte *e=(ubyte*)(ROM_BASE+ord+60);
	uint m=rx[2]+(rx[3]<<16);
	uint n=0;
	while(p<e)
		n+=*p++;
	return m==n;
}

static void jump(void){
	usbOff();
	displayOff();
//	for(ubyte i=255;i;i--)
//		__WFE();
//	(*(tagetBas+1))();
	FLASH_Unlock();
	flashErasePage(tempVect);
	flashCpy(tempVect,bootVect,1024);
	NVIC_SetVectorTable(NVIC_VectTab_FLASH,0x8000);

	flashErasePage(bootVect);
	flashCpy(bootVect,tagetBas,1024);
	FLASH_Lock();
	while(1)
		;
//	reboot();
}
static void reboot(void){
	usbOff();
	while(1)
		;
}
static void writeFirm(ushort idx,uint *code){
	FLASH_Unlock();
	ubyte *p=(ubyte*)((uint)idx+ROM_BASE);
	if(((idx+60)&0x3ff)<=60)
		flashErasePage(p+60);
	flashCpy(p,code,60);
	FLASH_Lock();
}

//------------------------------------------------------------------------------

void checkVecTable(void){
	if(*(bootVect+1)==*(vecTable+1))
		return;
	FLASH_Unlock();
	flashErasePage(bootVect);
	flashCpy(bootVect,vecTable,1024);
	FLASH_Lock();
	reboot();
}
uint flashCpy(void* dst,const void* src,uint size){
	if((uint)dst&1)						// not short aligned
		return 0;						// error
	uint ret=size;
	if(((uint)dst&2)&& size>1){			// not int aligned data
		FLASH_ProgramHalfWord((uint)dst,*(ushort*)src);
		dst=(void*)((uint)dst+2);
		src=(void*)((uint)src+2);
		size-=2;
	}
	for(;size>3;size-=4){
		FLASH_ProgramWord((uint)dst,*(uint*)src);
		dst=(void*)((uint)dst+4);
		src=(void*)((uint)src+4);
	}
	if(size>1){							// not int aligned data
		FLASH_ProgramHalfWord((uint)dst,*(ushort*)src);
		size-=2;
	}
	return ret-size;
}
void flashErasePage(void* dst){
	FLASH_ErasePage((uint)dst);
}
void iap(ushort *rx){
	static ubyte status;
	#define	UPG_READY	0
	#define	UPG_DOING	1
	#define	UPG_ERROR	2
	if(rx){
		static ushort orp;
		ushort ord=rx[1];
		if(ord==0xffff){
			if(checkSum(rx,orp))
				jump();				// never return
			status=UPG_ERROR;
			return;
		}
		switch(status){
		case UPG_READY:
			if(ord!=ordBase)
				goto err;
			status=UPG_DOING;
			break;
		case UPG_DOING:
			if(ord==(orp+60))
				break;
			else if(ord==orp)
				goto ack;
err:		status=UPG_READY;
			rx[1]=ordBase;
			goto ack;
		default:						// UPG_ERROR
			return;
		}
		writeFirm(ord,(uint*)(rx+2));
		orp=ord;						// save previous idx
ack:	sendReport(rx);
	}else status=UPG_READY;				// reset
}